相信大家在國中小的地理課一定有學過地圖,地圖中最重要也是容易讓人混淆的就是比例尺。思考一下,要如何在小小的A4紙當中塞進一個台灣?
比例就是為了解決這個問題,將台灣大小等比例縮小到可以放在A4當中,並說明地圖上的一公分代表的是實際的多長,那就是比例尺的功用。
在畫普通的圖表也是如此,如果要畫一個簡單的長條圖,前面我們都是已經定義好svg畫布的大小,假設是400 * 600,若有一筆資料的值是1000,那不就會遠遠超出畫布範圍了呢?
D3提供了scale的方法來讓你給的一組數據轉換輸出成另一組適合的數據,轉換方式又會因為資料特性而有不同,這邊先來介紹比較常用的linear,也就是線性比例。
這張圖的中間像工廠的地方就是d3的scale,實際大小在經過中間的轉換以後能得到在畫布上應該要顯示的大小,除了數字大小以外,d3也可以讓數字轉換成顏色的色碼,供我們可以做熱力圖等圖表。
直接來寫程式看看!
// 先做出轉換用的function,就是圖中中間的工廠
const y = d3.scaleLinear()
.domain([30, 1000]) //實際的最小值為30、最大值為1000
.range([0, 400]) //畫布長度的最小值為0、最大值為400
console.log(y(500)); //輸入實際的500,在畫布上為193.8144
現在你知道該怎麼做出scale的function了,那該怎麼應用在圖表上?
有一筆資料
[874, 291, 30, 500, 1000]
,請畫出長條圖。
<svg width="600" height="400" style="border: 1px solid black"></svg>
<script src="http://d3js.org/d3.v5.min.js"></script>
<script>
const data = [874, 291, 30, 500, 1000];
const svg = d3.select('svg');
const y = d3
.scaleLinear()
.domain([0, d3.max(data)])
.range([0, 400]);
const rects = svg
.selectAll('rect')
.data(data)
.enter()
.append('rect')
.attr('x', (d, i) => {
return i * 60
})
.attr('height', (d, i) => {
return y(d);
})
.attr('width', 40)
.attr('fill', 'black');
</script>
在畫上一張的長條圖時你有沒有覺得很奇怪,如果今天資料筆數每一次都不一樣,那我怎麼知道每個長條圖寬度要放多少?x是多少?彼此又要離多遠?
這時候就要用到d3的分段比例了,用法跟線性的很類似,使用的情境多在於圖表當中非線性的資料列。
廢話不多說,直接來實作看看:
<svg width="600" height="400" style="border: 1px solid black"></svg>
<script src="http://d3js.org/d3.v5.min.js"></script>
<script>
const data = [
{ value: 874, name: '1' },
{ value: 291, name: '2' },
{ value: 30, name: '3' },
{ value: 500, name: '4' },
{ value: 1000, name: '5' }
];
const svg = d3.select('svg');
const y = d3
.scaleLinear()
.domain([
0,
d3.max(data.map((e) => e.value))
])
.range([0, 400]);
//原本的x、width等都是寫死的,這次我們做一個x scale的function
const x = d3
.scaleBand()
.domain(data.map((e) => e.name))
.range([0, 600])
.paddingInner(0.3)
.paddingOuter(0.3);
const rects = svg
.selectAll('rect')
.data(data)
.enter()
.append('rect')
.attr('x', (d, i) => {
return x(d.name);
}) //將name直接丟進去x()
.attr('width', x.bandwidth) // 寬度直接使用它幫我們算好的
.attr('height', (d, i) => {
return y(d.value);
})
.attr('fill', 'black');
如此一來就得到x平均分配的圖表呢!